// @name        OH repo Dialog 
// @namespace   Violentmonkey Scripts
// @grant       none
// @version     1.0.1
// @author      milkpotatoes
// @require     ./utils.js
// @homepageURL https://gitee.com/milkpotatoes/tools-for-openharmony-repo
// @description Toast for openharmony repo tools
// ==/UserScript==

// import Utils from './utils'

const DIALOG_STYLE_SHEET = `
:host {
    --button-text-color: 0, 110, 28;
    --accent-button-text-color: 17, 31, 15;
    --button-bg-color: transparent;
    --accent-button-bg-color: 213, 232, 207;
    z-index: 9999;
}

:host,
.alert-dialog-overlay {
    display: block;
    position: fixed;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
}

.alert-dialog-overlay {
    background: #0003;
}

.alert-dialog-body {
    padding: 1.5em;
    background: #e8e9e2;
    display: flex;
    position: fixed;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    border-radius: 2em;
    box-shadow: 0 0 8px -2px #0005;
    box-sizing: border-box;
    flex-direction: column;
    overflow: hidden;
    gap: .5em;
    max-width: 100%;
    max-height: 100%;
}

.alert-dialog-body>* {
    display: flex;
    flex-direction: row;
}
.alert-dialog-title-icon
{
    user-select: none;
    transition: .2s ease-out;
    width: 1.8em;
    height: 1.8em;  text-align: center;
  border-radius: 50%;
  line-height: 1.8em;
    }
    .alert-dialog-title-icon:hover {
    background: rgba(var(--button-text-color), .08);
    }
.alert-dialog-header>h2 {
    margin: 0;
    padding: 0;
    font-size: 1.5em;
    font-weight: normal;
    flex-grow: 1;
    text-transform: uppercase;
}

.alert-dialog-footer,
.alert-dialog-header {
    height: 56px;
    align-items: center;
    justify-content: center;
    gap: 1em;
}

.alert-dialog-footer {
    height: 64px;
    flex-direction: row;
    justify-content: end;
    align-items: center;
}

.alert-dialog-title-button,
.alert-dialog-footer>button {
    color: rgb(var(--button-text-color));
    font-size: 1.2em;
    background: rgba(var(--button-bg-color), 1);
    border: none;
    position: relative;
    --hover-bg-color: transparent;
    box-sizing: border-box;
    padding: .8em 1em;
    min-width: 4em;
    border-radius: 2em;
    overflow: hidden;
    transition: .1s ease-in;
    text-transform: uppercase;
}

.alert-dialog-title-button:hover,
.alert-dialog-footer>button:hover {
    --hover-bg-color: rgba(var(--button-text-color), .08);
}
.alert-dialog-footer>button:hover[accent]
{
    box-shadow: 0 2px 4px -1px rgba(0, 0, 0, .2);
}
.alert-dialog-title-button::before,
.alert-dialog-footer>button::before {
    content: "";
    display: block;
    background: var(--hover-bg-color);
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
}

.alert-dialog-title-button,
.alert-dialog-footer>button[accent] {
    color: rgb(var(--accent-button-text-color));
    background: rgba(var(--accent-button-bg-color), 1);
}

.alert-dialog-content {
    flex-grow: 1;
    flex-direction: column;
    justify-content: start;
    align-items: start;
    white-space: pre-wrap;
}
`;
const DIALOG_STYLE_SHEET_URL = URL.createObjectURL(new Blob([DIALOG_STYLE_SHEET]));
const DIALOG_ELEMENT_TEMPLATE = `
    <link rel="stylesheet" href="https://unpkg.com/material-icons@1.13.12/iconfont/material-icons.css">
    <link rel="stylesheet" href="${DIALOG_STYLE_SHEET_URL}">
    <div class="alert-dialog-overlay"></div>
    <div class="alert-dialog-body">
        <header class="alert-dialog-header" style="display: none">
            <span class="material-icons alert-dialog-title-icon" style="display: none">close</span>
            <h2 class="alert-dialog-title" style="display: none"></h2>
            <button class="alert-dialog-title-button" style="display: none" accent></button>
        </header>
        <div class="alert-dialog-content" style="display: none"></div>
        <footer class="alert-dialog-footer" style="display: none"></footer>
    </div>
`;

class AlertDialogData {
    constructor(dialog) {
        this.shadow = dialog.attachShadow({ mode: 'closed' });
    }
    shadow;
    isModal = false;
}

const alertDialogWeakMap = new WeakMap();
let g_materialIconFontInitd = false;
const initMaterialIconFont = () => {
    if (g_materialIconFontInitd) {
        return;
    }
    document.fonts.add(new FontFace('Material Icons',
        `url("https://unpkg.com/material-icons@1.13.12/iconfont/material-icons.woff") format("woff2"), ` +
        `url("https://unpkg.com/material-icons@1.13.12/iconfont/material-icons.woff2") format("woff")`));
}

/**
 * 
 * @param {AlertDialog} dialog name of icon, must be in material icon
 * @param {string} button name of icon, must be in material icon
 * @param {{events: [Iterable<string, (Event) => boolean], accent: boolean, dismissOnClick: boolean}?} option
 */
const buttonAddEvents = (dialog, button, option) => {
    if (option?.dismissOnClick) {
        button.addEventListener('click', () => setTimeout(() => { dialog.dismiss() }, 0));
    }
    if (option?.events === undefined || option?.events === null) {
        return;
    }
    for (const ev in option?.events) {
        button.addEventListener(ev, (e) => {
            if (option.events[ev](e)) {
                dialog.dismiss();
            };
        });
    }
}

class AlertDialog extends HTMLElement {
    constructor() {
        super();
        const data = new AlertDialogData(this);

        alertDialogWeakMap.set(this, data);
        data.shadow.innerHTML = DIALOG_ELEMENT_TEMPLATE;
        data.shadow.querySelector('.alert-dialog-overlay').addEventListener('click', () => {
            if (!data.isModal) {
                this.dismiss();
            }
        });
    }

    show() {
        this.dispatchEvent(new Event('show'))
        this.style.opacity = 1;
        document.body.append(this);
        const body = alertDialogWeakMap.get(this).shadow.querySelector('.alert-dialog-body');
        body.animate([
            { transform: "translate(-50%, -75%) rotateX(90deg)" }, {}
        ], {
            duration: 500,
            easing: "ease-out"
        });
    }

    dismiss() {
        this.dispatchEvent(new Event('dismiss'));
        const body = alertDialogWeakMap.get(this).shadow.querySelector('.alert-dialog-body');
        body.animate([
            {}, { transform: "translate(-50%, -75%) rotateX(90deg)" }
        ], {
            duration: 200,
            easing: "ease-in"
        });
        this.animate([
            { opacity: 1 }, { opacity: 0 }
        ], {
            duration: 200,
            easing: "ease-in"
        });
        this.style.opacity = 0;
        this.style.pointerEvents = 'none';
        setTimeout(() => {
            this.remove();
        }, 200);
    }
    /**
     * 
     * @param {string} title 
     * @returns {AlertDialog}
     */
    setTitle(title) {
        const header = alertDialogWeakMap.get(this).shadow.querySelector('.alert-dialog-header');
        header.style.display = '';
        const titleBar = header.querySelector('h2');
        titleBar.style.display = '';
        titleBar.textContent = title;
        return this;
    }

    setModal(modal) {
        alertDialogWeakMap.get(this).isModal = modal;
        return this;
    }

    /**
     * 
     * @param {string} content 
     * @param {{events: [Iterable<string, (Event) => boolean], accent: boolean, dismissOnClick: boolean}?} option
     */
    setContent(content) {
        const body = alertDialogWeakMap.get(this).shadow.querySelector('.alert-dialog-content');
        body.style.display = '';
        body.textContent = content;
        return this;
    }

    setView(view) {
        const body = alertDialogWeakMap.get(this).shadow.querySelector('.alert-dialog-content');
        body.style.display = '';
        if (view instanceof HTMLElement) {
            body.innerHTML = '';
            body.append(view);
        } else {
            body.innerHTML = view;
        }
        return this;
    }

    setWidth(width) {
        if (typeof width === 'number') {
            width = `${width}px`;
        }
        alertDialogWeakMap.get(this).shadow.querySelector('.alert-dialog-body').style.width = width;
        return this;
    }

    setHeight(height) {
        if (typeof height === 'number') {
            height = `${height}px`;
        }
        alertDialogWeakMap.get(this).shadow.querySelector('.alert-dialog-body').style.height = height;
        return this;
    }

    onDismiss(func) {
        this.addEventListener('dismiss', func);
        return this;
    }

    onShow(func) {
        this.addEventListener('show', func);
        return this;
    }

    /**
     * 
     * @param {string} text 
     * @param {{events: [Iterable<string, (Event) => boolean], accent: boolean, dismissOnClick: boolean}?} option
     */
    addButton(text, option) {
        const button = document.createElement('button');
        button.textContent = text;
        if (option?.accent) {
            button.setAttribute('accent', '');
        }
        const footer = alertDialogWeakMap.get(this).shadow.querySelector('.alert-dialog-footer');
        footer.style.display = '';
        footer.append(button);
        buttonAddEvents(this, button, option);
        return this;
    }

    /**
     * 
     * @param {string} icon name of icon, must be in material icon
     * @param {{events: [Iterable<string, (Event) => boolean], accent: boolean, dismissOnClick: boolean}?} option
     */
    setIcon(icon, option) {
        initMaterialIconFont();
        const header = alertDialogWeakMap.get(this).shadow.querySelector('.alert-dialog-header');
        header.style.display = '';
        const ico = header.querySelector('.alert-dialog-title-icon');
        ico.style.display = '';
        ico.textContent = icon;
        buttonAddEvents(this, ico, option);
        return this;
    }

    /**
     * 
     * @param {string} icon name of icon, must be in material icon
     * @param {{events: [Iterable<string, (Event) => boolean], accent: boolean, dismissOnClick: boolean}?} option
     */
    setHeaderButton(text, option) {
        initMaterialIconFont();
        const header = alertDialogWeakMap.get(this).shadow.querySelector('.alert-dialog-header');
        header.style.display = '';
        const btn = header.querySelector('.alert-dialog-title-button');
        btn.style.display = '';
        btn.textContent = text;
        buttonAddEvents(this, btn, option);
        return this;
    }

    static async alert(title, message, confirm = 'OK') {
        if ((title === undefined || title === null || title === '') &&
            (message === undefined || message === null || message === '')) {
            throw new TypeError('invalid arguments, both title and message are empty');
        }
        if ((confirm === undefined || confirm === null || confirm === '')) {
            throw new TypeError('invalid arguments, button text is empty');
        }
        return new Promise((r) => {
            const dialog = new AlertDialog()
                .onDismiss(() => r(undefined))
                .addButton(confirm, { dismissOnClick: true, accent: true });
            if (title !== '') {
                dialog.setTitle(title);
            }
            if (typeof message === 'string' && message !== '') {
                dialog.setContent(message);
            } else if (message instanceof HTMLElement) {
                dialog.setView(message);
            }
            dialog.show();
        });
    }

    static async confirm(title, message, confirm = 'OK', cancel = 'CANCEL') {
        if ((title === undefined || title === null || title === '') &&
            (message === undefined || message === null || message === '')) {
            throw new TypeError('invalid arguments, both title and message are empty');
        }
        return await new Promise((r) => {
            const dialog = new AlertDialog()
                .addButton(cancel, { dismissOnClick: true })
                .addButton(confirm, {
                    accent: true, events: {
                        click() {
                            r(true);
                            dialog.dismiss();
                        },
                    }
                })
                .onDismiss(() => {
                    r(false);
                });
            if (title !== '') {
                dialog.setTitle(title);
            }
            if (message !== '') {
                dialog.setContent(message);
            }
            dialog.show();
        });
    }

    static async prompt(title, placeholder = '', defaults = '', confirm = 'OK', cancel = 'CANCEL') {
        if ((title === undefined || title === null || title === '')) {
            throw new TypeError('invalid arguments, title is empty');
        }
        if ((confirm === undefined || confirm === null || confirm === '') ||
            (cancel === undefined || cancel === null || cancel === '')) {
            throw new TypeError('invalid arguments, button text is empty');
        }
        const input = document.createElement('input');
        input.value = defaults;
        input.placeholder = placeholder;
        input.style.cssText = 'width:100%;font-size:1.5em;height:1em;background:rgba(0,0,0,.05);border:none;' +
            'border-radius:.5em;padding:1em;box-sizing:border-box;overflow:auto';
        input.addEventListener('keydown', (e) => {
            e.stopPropagation();
        });
        return await new Promise((r) => {
            const dialog = new AlertDialog()
                .setView(input)
                .setTitle(title)
                .addButton(cancel, { dismissOnClick: true })
                .addButton(confirm, {
                    accent: true, events: {
                        click() {
                            r(input.value);
                            dialog.dismiss();
                        },
                    }
                }).onDismiss(() => {
                    r(null);
                });
            dialog.show();
        });
    }
}

customElements.define("tool4oh-repo-alert-dialog", AlertDialog);
